#include "./logic_include/file_uploader.h"
#include "../include/conf_reader.h"
#include "../include/my_log.h"
#include <unistd.h>
#include "../include/macro.h"
#include "logic_include/logic_macro.h"
file_uploader * file_uploader::now_file_uploader=nullptr;   //静态成员初始化

file_uploader_thread_item::file_uploader_thread_item(file_uploader* this_file_uploader){
    
    this->this_file_uploader=this_file_uploader;
    this->is_running=false;
    pthread_mutex_init(&this->mutex,NULL);
    pthread_cond_init(&this->cond_t,NULL);
    
}

file_uploader_thread_item::~file_uploader_thread_item()
{
    //此时task_queue里应该应该不会有数据，处理完了
    pthread_mutex_destroy(&this->mutex);
    pthread_cond_destroy(&this->cond_t);
    for(auto it=this->fd_map.begin();it!=fd_map.end();++it){ 
        //其实已经保证了线程在退出前，数据全部处理完,但是并没有保证未传输完的fd关闭，所以在这里关闭了

        close(it->second->fd);
        delete it->second;
    }
    this->fd_map.clear();   //直接清空
}


file_uploader::file_uploader()
{
    //根据数量大小创建任务变量
    this->thread_num=atoi(conf_reader::get_conf_reader_instance()->get_value_from_key("file_uploader_num","5"));
    this->is_end_file_uploader_thread=FILE_UPLOADER_THREAD_RUNNING;
    for(int i=0;i<thread_num;++i){
        file_uploader_thread_item * item=new file_uploader_thread_item(this);

        this->thread_item_list.push_back(item);
        //开启工作线程
        pthread_create(&item->handler,NULL,file_uploader::file_uploader_thread,item);
    }
   
    //确保线程启动
    for(int i=0;i<thread_num;++i){
        if(this->thread_item_list[i]->is_running==false){
            usleep(100*1000);   //100毫秒
            --i;
        }
    }
    my_log::get_log_instance()->write_log(LOG_INFO,0,"文件上传线程成功启动");
}


void *file_uploader::file_uploader_thread(void * _thread)
{
    file_uploader_thread_item * item=(file_uploader_thread_item *)(_thread);
    file_uploader * _file_uploader=item->this_file_uploader;
    file_upload_message * now_msg;
    
    while(1){   //该线程怎么退出还没考虑好----通过后续判断是否结束来结束
        pthread_mutex_lock(&item->mutex);   //先上锁
        while(item->task_queue.empty()&& _file_uploader->is_end_file_uploader_thread==FILE_UPLOADER_THREAD_RUNNING){
            item->is_running=true;
            pthread_cond_wait(&item->cond_t,&item->mutex); 
            //避免意外唤醒----为什么存在意外唤醒？？
        }
        
        //醒来之后，循环将数据写入

        while(!item->task_queue.empty()){

            now_msg=item->task_queue.front();
            item->task_queue.pop_front();
            std::string path_name=now_msg->path()+now_msg->file_name();
            auto it=item->fd_map.find(path_name);
            //能进来，那么必定是存在的
            // if(it==item->fd_map.end()){
            //     //由于考虑到断点续传的缘故，这里依旧要做判断
            //     delete now_msg;
            // }

            lseek(it->second->fd,now_msg->offset(),SEEK_SET);
            std::string tmp=now_msg->data();  //进行拷贝构造，减小开销

            int len=tmp.length();
            int write_res=write(it->second->fd,tmp.c_str(),len);
            if(write_res<len){
                my_log::get_log_instance()->write_log(LOG_ALERT,errno,"文件切片写入失败?");
                item->task_queue.push_back(now_msg);    //重新放入后面，可能是信号中断导致的 
                continue;
            }
            
            it->second->transformed_bytes+=len;

            (it->second->con_p->wait_to_deal_received_pkgs)--;        //该链接对应的待处理的包减少1

            // it->second->wait_to_write_pkg--;
            // it->second->write_package_num++;
            // if(it->second->write_package_num==(now_msg->seq()+1)){   //由于seq是从0开始的
            //     it->second->last_seq_equal=now_msg->seq();
            // }
            
            
            
            delete now_msg; //将这条数据释放掉----------------

            if(it->second->transformed_bytes==it->second->file_size){
                //这就表示接收完毕了---写日志说明一下
                my_log::get_log_instance()->write_log(LOG_INFO,0,"成功接收文件 %s  大小一共为 %d 字节",path_name.c_str(),it->second->file_size);
                //将该fd关闭掉，并从fd_map中移除以释放资源
                
                close(it->second->fd);   //关闭文件描述符
                _file_uploader->all_info_fd_map.erase(path_name);   //从全局里删掉
                delete it->second;
                item->fd_map.erase(it);
                //可以考虑将文件写入完毕后，发送一个上传完毕的通知给客户端---------------------目前没有实现
            }
            


            //我如何保证已传输的切片写入完全（由于采用多线程拿初始包）?  不然我发送的断点信息没有意义
            // if(it->second.icurrentsequence!=it->second.con_p->icurrentsequence){
            //     //断连了，将当前的it放入全局的断点集合里
            //     _file_uploader->all_break_info_fd_map.insert(*it);
            //     item->fd_map.erase(it); 
            // }

            
            //这里也记录一下写入字节的的信息
            // my_log::get_log_instance()->write_log(LOG_INFO,0,"成功写入文件 %s  该切片大小为%d",now_msg->file_name(),len);
           
            

        }
        
        pthread_mutex_unlock(&item->mutex);

        if(_file_uploader->is_end_file_uploader_thread==FILE_UPLOADER_THREAD_STOP){
            break;
        }

    }



    return NULL;
}

void file_uploader::end_file_uploader_thread()
{
    this->is_end_file_uploader_thread=FILE_UPLOADER_THREAD_STOP;
    for(int i=0;i<thread_num;++i){
        pthread_join(this->thread_item_list[i]->handler,NULL);
        delete this->thread_item_list[i];   //执行相应的析构函数会释放对应的变量
    }
    my_log::get_log_instance()->write_log(LOG_INFO,0,"文件上传处理线程关闭！！");
}